package com.jiangds.open.check;

import com.jiangds.open.annotation.CheckDeep;
import com.jiangds.open.annotation.CheckRule;
import com.jiangds.open.check.processor.ProcessorType;
import com.jiangds.open.check.processor.SingleProcessor;
import com.jiangds.open.util.AnnotationUtil;
import com.jiangds.open.util.FieldWrapper;
import com.jiangds.open.util.TypeRegister;
import java.lang.reflect.Field;
import java.util.*;

/**
 * {@link Check}  构建类,
 * 校验方法流程已经完全确定,见{@link ProcessorType},
 * 单项规则校验方案采用默认的方法,
 * 如果需要自己定义对应的方案
 *
 * @author jiangliang
 * @since 2021/1/5 14:08
 */
public final class Check {

    private SingleProcessor singleProcessor;

    public Check(SingleProcessor singleProcessor) {
        this.singleProcessor = singleProcessor;
    }

    /**
     * 对有annotation的对象进行校验,也校验继承的属性
     * 如果当前类存在{@link CheckDeep} 注解,
     * 则会递归检验该类属性为{@code Object} 或 {@code Collection<Object>} 中的Object对象
     * 如果递归过程中的Object对象不再拥有CheckDeep,则不进行下一层的递归校验,否则一直校验到没有 {@link CheckDeep} 注解结束
     *
     * @param target 对象
     */
    public void checkObject(Object target) {
        List<Field> allInstanceField = FieldWrapper.getAllInstanceField(target);
        for (Field field : allInstanceField) {
            Class<?> type = field.getType();
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            Object targetValue = null;
            try {
                targetValue = field.get(target);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            List<CheckRule> checkRules = AnnotationUtil.checkRules(field);
            boolean hasDeep = type.isAnnotationPresent(CheckDeep.class);
            if (TypeRegister.inCheckType(type)) {
                singleProcessor.annotationCheck(type, targetValue, checkRules);
            } else {
                if (TypeRegister.inCollection(type)) {
                    singleProcessor.annotationCheck(type, targetValue, checkRules);
                    if (targetValue != null && hasDeep) {
                        for (Object o : ((Collection) targetValue)) {
                            checkObject(o);
                        }
                    }
                } else if (hasDeep) {
                    checkObject(targetValue);
                }
            }
        }
    }

}
